#include "TcpServer.h"

using namespace std;
using namespace network;

TcpServer::TcpServer(recvCallBack recv_cb, unsigned int port, Reactor *reactor) : recv_cb_(std::move(recv_cb)),
    reactor_(reactor) {
    server_conn_ = make_shared<ServerConnection>();
    server_conn_->setPort(port);
}

TcpServer::~TcpServer() {
    stop();
}

void TcpServer::start() {
    init();
    reactor_->loop();
}

void TcpServer::stop() {
    reactor_->stop();
}

void TcpServer::init() {
    if (recv_cb_ == nullptr) {
        throw runtime_error("the callback functions for receiving messages are not set");
    }
    if (!server_conn_->setPortReuable()) {
        throw runtime_error("the port multiplexing setting failed");
    }
    if (!server_conn_->bind()) {
        throw runtime_error("failed to bind the port");
    }
    if (!server_conn_->listen()) {
        throw runtime_error("listening failed");
    }
    server_conn_->setNonBlock();
    auto server_handler = bind(&TcpServer::serverHandler, this, placeholders::_1, placeholders::_2,
                               placeholders::_3);
    if (!reactor_->registerHandler(server_conn_->getFd(), EventType::Read, server_handler, server_conn_.get())) {
        throw runtime_error("Reactor register handler failed");
    }
}

void TcpServer::serverHandler(int fd, EventType event, void *context) {
    ServerConnection *server_conn = static_cast<ServerConnection*>(context);
    shared_ptr<ClientConnection> client;
    try {
        client = make_shared<ClientConnection>(server_conn->accept(), true, reactor_);
        client->setRecvCallBack(recv_cb_);
        if (close_cb_ != nullptr) {
            client->setCloseCallBack(close_cb_);
        }
        client->setNonBlock();
        if (accept_cb_ != nullptr) {
            accept_cb_(client.get());
        }
        clients_.emplace(client->getFd(), client);
        reactor_->registerHandler(client->getFd(), EventType::Read,
                                  bind(&TcpServer::clientHandler, this, placeholders::_1, placeholders::_2,
                                       placeholders::_3), nullptr);
    }
    catch (const exception &e) {}
}

void TcpServer::clientHandler(int fd, EventType event, void *context) {
    try {
        std::shared_ptr<ClientConnection> client = clients_.at(fd);
        if (event == EventType::Read) {
            client->clientRecv();
        }
        else if (event == EventType::Write) {
            client->clientSend();
        }
        else if (event == EventType::Close) {
            client->clientClose();
            clients_.erase(fd);
        }
    }
    catch (const exception &e) {}
}
